/*

    This file is part of Hometree.

    Hometree is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Hometree is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Hometree.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2011,2012 Roman Held

*/

/********************************************************************\
                            Includes
\********************************************************************/
#include <QDebug>
#include <QtPlugin>
#include <QMetaProperty>
#include <QDir>
#include "persistentservice.h"
#include "htr/htrlog.h"

/********************************************************************\
                            Defines
\********************************************************************/
#define CACHE_VERSION   1                     // Cache-Format
#define STREAM_VERSION  QDataStream::Qt_4_6   // Stream-Format

#define IDENT "PSFLE"
#ifdef Q_WS_WIN
#define ROOT_DIR "Hometree"
#else
#define ROOT_DIR ".hometree"
#endif
#define CACHE_DIR "filecache"

/********************************************************************\
                            Implementation
\********************************************************************/

/*-------------------------------------------------------------------
*/
PersistentService::PersistentService(QObject *parent)
 : HtrPersistentService("filecache", parent)
{
  initFileCache();
}

/*-------------------------------------------------------------------
*/
void PersistentService::store(QObject *obj)
{
  if (mCacheDir.length() == 0) // error on init?
    return;

  Q_ASSERT(obj && obj->metaObject());
  Q_ASSERT(obj->objectName().length() > 0);
  const QMetaObject *m = obj->metaObject();

  QString fileName = mCacheDir + QDir::separator() + obj->objectName();
  QFile f(fileName);
  bool done = f.open(QIODevice::WriteOnly);
  if (!done) {
    HtrLog::instance()->warning(IDENT,tr("Error opening %1").arg(fileName));
    return; 
  }
  
  QDataStream out(&f); 
  out.setVersion(STREAM_VERSION);
  
  out << (qint32)CACHE_VERSION;
  out << QString(obj->objectName());
  out << QString(m->className());
  
  for(int i=0; i < m->propertyCount(); i++) {
    QMetaProperty p = m->property(i);
    if (p.isStored()) {
      HtrLog::instance()->info(IDENT,tr("store: %1: Property %2").arg(m->className()).arg(p.name()));
      out << QString(p.name());
      out << p.read(obj); 
    }
  }

  // that's it..
}

/*-------------------------------------------------------------------
*/
QList<QObject*> PersistentService::restoreAll()
{
  QList<QObject*> ret;
  if (mCacheDir.length() == 0) // error on init?
    return ret;

  QDir dir(mCacheDir);
  QFileInfoList files =  dir.entryInfoList(QDir::Files | QDir::Readable);
  foreach(QFileInfo next, files) {
    QFile f(next.absoluteFilePath());
    bool done = f.open(QIODevice::ReadOnly);
    if (!done) {
      HtrLog::instance()->warning(IDENT,tr("Error opening %1").arg(next.absoluteFilePath()));
      continue;
    }
    QDataStream in(&f); 
    in.setVersion(STREAM_VERSION);

    QObject *nextObj=setupObject(in);
    if (nextObj) 
      ret << nextObj;
  }
  return ret;
}

/*-------------------------------------------------------------------
*/
void PersistentService::initFileCache()
{
  QDir dir(QDir::homePath());
  dir.mkdir(ROOT_DIR);
  bool done = dir.cd(ROOT_DIR);
  dir.mkdir(CACHE_DIR);
  done = dir.cd(CACHE_DIR);
  if (done) {
    mCacheDir = dir.absolutePath();
    HtrLog::instance()->info(IDENT,tr("Cachepath: %1").arg(mCacheDir));
  } else
    HtrLog::instance()->warning(IDENT,tr("Cannot create Cachepath"));
}

/*-------------------------------------------------------------------
*/
QObject *PersistentService::setupObject(QDataStream &in)
{
  QObject *ret = NULL;
  qint32 version;
  in >> version; 
  switch (version) {
    case 1 : {
       QString objectName;
       QString className;
       in >> objectName; 
       in >> className; 
       ret = create(className);
       if (!ret)    // Invalid class... FIXME?
         return ret;
       ret->setObjectName(objectName);
       while (!in.atEnd()) {
         QString  propertyName;
         QVariant propertyValue;
         in >> propertyName;
         in >> propertyValue;
         setObjectProperty(ret,propertyName,propertyValue);
       }
      } break; // case Version 1
    default:; // FIXME: Error-Handling?
  }
  return ret; 
}

/*-------------------------------------------------------------------
*/
void PersistentService::clear(QObject *obj)
{
  if (mCacheDir.length() == 0) // error on init?
    return;

  Q_ASSERT(obj);
  Q_ASSERT(obj->objectName().length() > 0);
  const QMetaObject *m = obj->metaObject();

  QString fileName = mCacheDir + QDir::separator() + obj->objectName();
  if (QFile::exists(fileName)) {
    HtrLog::instance()->info(IDENT,tr("removing object %1").arg(obj->objectName()));
    QFile::remove(fileName);
  }
}

/*-------------------------------------------------------------------
*/
void PersistentService::setObjectProperty(QObject *obj, const QString &pName, const QVariant &pValue) 
{
  Q_ASSERT(obj);
  Q_ASSERT(pName.length() > 0);

  const QMetaObject *m = obj->metaObject();
  for (int i=0; i < m->propertyCount(); i++) {
    if (QString(m->property(i).name()) == pName) {
      Q_ASSERT(m->property(i).isWritable());
      bool done = m->property(i).write(obj,pValue);
      if (!done)
        HtrLog::instance()->warning(IDENT,tr("Unknown Error: cannot write property: %1").arg(pName));
      break;
    }
  }
}

Q_EXPORT_PLUGIN2(plgservicefile,PersistentService);
